<?php

/**
 * 
 * 
 * 
 * 
 */
abstract class ConexionBD {

    private static $host;
    private static $user;
    private static $password;
    protected $database;
    protected $query;
    protected $rows;
    private $link;
    private $last_id;

    /**
     * Si no se introducen los parametros, ira al archivo Config
     * 
     * @param string $host 
     * @param string $user
     * @param string $password
     * @param string $database
     */
    function __construct($host = '', $user = '', $password = '', $database = '') {
        self::$host = $host !== '' ? $host : Config::$host;
        self::$user = $user !== '' ? $user : Config::$user;
        self::$password = $password !== '' ? $password : Config::$password;
        $this->database = $database !== '' ? $database : Config::$database;
        $this->query = '';
        $this->rows = array();
        $this->link = NULL;
        $this->last_id = NULL;
    }

    abstract public function get_datos($id);

    abstract public function insertar_datos($datos = array());

    abstract public function editar($datos = array());

    abstract public function borrar($id);

    private function abrir_conexion() {
        $this->link = new mysqli(self::$host, self::$user, self::$password, $this->database);
        /*
         * PARA LANZAR EXCEPCION DE FALLO EN LA CONEXION
         * En la version OO siempre devuelve un objeto por lo que para saber si la
         * conexion a fallado hay que utilizar la propiedad connect_error:
         * Esta seria la forma OO correcta:
         * 
         * if ($this->link->connect_error)
         * throw new Exception('Error al conectar con la base de datos ['
         * . $this->link->connect_errno . ']');
         * 
         * Sin embargo antes de php 5.2.9 y 5.3.0 $connect_error estaba averiado
         * Por lo que es recomendable utilizar el estilo por procedimientos para que
         * funcione en cualquier version php:
         */
        if (mysqli_connect_error()) {
            throw new Exception('Error al conectar con la base de datos ['
            . $this->link->connect_errno . ']' . mysqli_connect_error());
        }
        if (!$this->link->set_charset('utf8')) {
            $array_error = $this->link->error_list;
            $array_error = var_export($array_error, TRUE);
            throw new Exception('Error al establecer charset ['
            . $array_error . ']');
        }
    }

    private function cerrar_conexion() {
        if (!$this->link->close()) {
            throw new Exception('Error al cerrar la base de datos');
        }
    }

    protected function ejecutar_query() {
        /*
         * con xdebug para evitar excepcion 
         */
        if ($this->link === NULL) {
            $this->abrir_conexion();
        }
        if (!$this->link->query($this->query)) {
            $array_error = $this->link->error_list;
            $array_error = var_export($array_error, TRUE);
            throw new Exception('Error al ejecutar consulta ['
            . $array_error . '] [' . $this->query . ']');
        }
        /*
         * mysqli_insert_id($this->link)
         * OO: $this->link->insert_id;
         * Si la ultima query no fue INSERT o UPDATE o la tabla modificada no tiene
         * una columna con el atributo AUTO_INCREMENT el insert_id sera 0
         */
        $this->last_id = $this->link->insert_id;
        /*
         * comentado durante xdebug para evitar excepcion 
         */
        /* $this->cerrar_conexion(); */
    }

    protected function get_resultado_query() {
        /*
         * con xdebug para evitar excepcion 
         */
        if ($this->link === NULL) {
            $this->abrir_conexion();
        }
        $result = $this->link->query($this->query, MYSQLI_STORE_RESULT);
        if ($result === FALSE) {
            $array_error = $this->link->error_list;
            $array_error = var_export($array_error, TRUE);
            throw new Exception('Error al ejecutar consulta ['
            . $array_error . '] [' . $this->query . ']');
        }
        /*
         * OTRA FORMA
         * $this->rows = $result->fetch_all(MYSQLI_ASSOC);
         * 
         */
        $this->rows = array();
        while ($row = $result->fetch_assoc()) {
            $this->rows[] = $row;
        }
        /*
         * comentado durante xdebug para evitar excepcion 
         */
        /* $result->free(); */
        /* $this->cerrar_conexion(); */
    }

    public function get_last_id() {
        return $this->last_id;
    }

}

/**
 * 
 * 
 * 
 * 
 */
class Usuario extends ConexionBD {

    const ACTIVADO = 'ACTIVADO';
    const FUE_ACTIVADO = 'FUE_ACTIVADO';
    const NO_ACTIVADO = 'NO_ACTIVADO';

    /*
     * Las siguientes propiedades tienen el mismo nombre que los campos de la tabla
     */

    private $id;
    private $email;
    private $clave;
    public $nombre;
    public $apellidos;
    public $facebook;
    public $activacion;
    public $codigo_activacion;
    public $codigo_acceso;
    public $fecha_registro;
    public $foto;

    function __construct($host = '', $user = '', $password = '', $database = '') {
        /*
         * 
         * En el contructor de la clase padre inicializo 
         * las variables de conexion a la base de datos
         */
        parent::__construct($host, $user, $password, $database);
    }

    public function get_email() {
        return $this->email;
    }

    public function get_id() {
        return $this->id;
    }

    public function borrar($id, $evitar_inyeccion = TRUE) {
        if ($evitar_inyeccion) {
            $id = evitar_inyeccion($id);
        }
        $this->query = "DELETE FROM `$this->database`.`usuarios` 
            WHERE `usuarios`.`id` = '$id'";
        $this->ejecutar_query();
    }

    public function editar($datos = array(), $evitar_inyeccion = TRUE) {
        /*
         * creo variables locales con "variables variables"
         */
        if ($evitar_inyeccion) {
            foreach ($datos as $key => $value) {
                $$key = evitar_inyeccion($value);
            }
        } else {
            foreach ($datos as $key => $value) {
                $$key = $value;
            }
        }
        $this->query = "UPDATE `$this->database`.`usuarios` SET 
            `email` = '$email',
            `clave` = '$clave',
            `nombre` = '$nombre',
            `apellidos` = '$apellidos',
            `facebook` = '$facebook',
            `activacion` = '$activacion',
            `codigo_activacion` = '$codigo_activacion',
            `codigo_acceso` = '$codigo_acceso',
            `fecha_registro` = '$fecha_registro',
            `foto` = '$foto' WHERE `usuarios`.`id` = '$id'";
        $this->ejecutar_query();
    }

    /**
     * Busca en la tabla `usuarios` el email pasado por parametro y cambia 
     * el estado del objeto con los datos de la fila encontrada
     * 
     * @param string $email email del usuario a buscar
     * @return boolean TRUE en caso de encontrar el email y FALSE si no se ha localizado
     */
    public function get_datos($email, $evitar_inyeccion = TRUE) {
        $email = strtolower($email);
        if ($evitar_inyeccion) {
            $email = evitar_inyeccion($email);
        }
        $this->query = "SELECT * FROM `usuarios` WHERE `email` = '$email'";
        $this->get_resultado_query();
        /*
         * Ya que count() 
         * devuelve 1 si $this->rows no es un array
         * Si es NULL devuelve 0
         */
        if (is_array($this->rows) && count($this->rows) === 1) {
            /*
             * Cambio estado del objeto con 'variables variables'
             */
            foreach ($this->rows[0] as $key => $value) {
                $this->$key = $value;
            }
            return TRUE;
        }
        return FALSE;
    }

    public function get_array_datos($id, $evitar_inyeccion = TRUE) {
        if ($evitar_inyeccion) {
            $id = evitar_inyeccion($id);
        }
        $this->query = "SELECT * FROM `usuarios` WHERE `id` = '$id'";
        $this->get_resultado_query();
        if (is_array($this->rows) && count($this->rows) === 1) {
            return $this->rows[0];
        }
        return FALSE;
    }

    /**
     * Inserta un nuevo usuario en la tabla `usuarios`.
     * 
     * @param array $datos Contiene los datos necesarios para crear el nuevo usuario.
     * Estos datos deben ser previamente validados.
     * La estructura de este array es la siguiente:
     * <pre>
     *     array (
     *         'email' => $email,
     *         'clave' => $clave,
     *         'nombre' => $nombre,
     *         'apellidos' => $apellidos,
     *         'foto' => $foto
     *         'facebook' => $facebook
     *     );
     * </pre>
     */
    public function insertar_datos($datos = array(), $evitar_inyeccion = TRUE) {
        /*
         * asigno valor a variables locales con "variables variables"
         */
        if ($evitar_inyeccion) {
            foreach ($datos as $key => $value) {
                $$key = evitar_inyeccion($value);
            }
        } else {
            foreach ($datos as $key => $value) {
                $$key = $value;
            }
        }
        date_default_timezone_set('Europe/Madrid');
        $fecha_registro = date_format(date_create(), 'Y-m-d H:i:s');
        $clave = md5($clave);
        $codigo_acceso = $clave . md5($email);
        $codigo_activacion = md5($email . time());

        $this->query = "INSERT INTO `$this->database`.`usuarios`
            (`id`, `email`, `clave`, `nombre`, `apellidos`, `facebook`, `activacion`,
            `codigo_activacion`, `codigo_acceso`, `fecha_registro`, `foto`) VALUES
            (NULL, '$email', '$clave', '$nombre', '$apellidos', '$facebook', '$activacion', 
            '$codigo_activacion', '$codigo_acceso', '$fecha_registro', '$foto')";
        $this->ejecutar_query();

        /*
         * Cambio estado del objeto
         */
        $this->get_datos($email);

        if ($activacion == 0) {  // envio email de activacion
            $to = $email;
            $subject = 'Activa tu cuenta';
            $action = 'activar_cuenta';
            $option = $codigo_activacion;
            $content = 'Para completar el registro deber&aacute;s hacer clic en el siguiente link:';
            enviar_email($to, $subject, $action, $option, $nombre, $content);
        }
    }

    /**
     * Metodo para comprobar si el login es correcto.
     * Tambien devuelve TRUE si es un usuario facebook
     * El objeto que llame a este metodo y reciba TRUE contrendra todos los datos del
     * usuario, ya que se llama al metodo get_datos
     * 
     * @param string $email email del usuario
     * @param string $clave clave sin codificar del usuario
     * @return boolean TRUE si el login es correcto o es un usuario facebook
     *  FALSE en caso contrario
     */
    public function comprobar_login($email, $clave) {
        if (validar_email($email) === FALSE || validar_texto($clave) === FALSE) {
            return FALSE;
        }
        if ($this->get_datos($email)) {
            if ($this->facebook != '0' || $this->clave == md5($clave)) {
                return TRUE;
            }
        }
        return FALSE;
    }

    /**
     * Metodo para activar la cuenta de un usuario
     * 
     * @param string $codigo_activacion Dato a buscar en la tabla `usuarios`
     * @return string 'FUE_ACTIVADO' si ya fue activado con anterioridad, 
     * 'ACTIVADO' si se ha activado correctamente o 
     * 'NO_ACTIVADO' si no existe ese codigo_activacion en la tabla `usuarios`
     */
    public function activar_cuenta($codigo_activacion) {
        $codigo_activacion = evitar_inyeccion($codigo_activacion);
        $this->query = "SELECT * FROM `$this->database`.`usuarios` "
                . "WHERE `codigo_activacion` = '$codigo_activacion'";
        $this->get_resultado_query();
        /*
         * Ya que count() 
         * devuelve 1 si $this->rows no es un array
         * Si es NULL devuelve 0
         */
        if (is_array($this->rows) && count($this->rows) === 1) {
            if ($this->rows[0]['activacion'] != '0') {
                return self::FUE_ACTIVADO;
            }
            $id = $this->rows[0]['id'];
            $this->query = "UPDATE `$this->database`.`usuarios` SET `activacion` = '1' "
                    . "WHERE `usuarios`.`id` = $id";
            $this->ejecutar_query();
            return self::ACTIVADO;
        }
        return self::NO_ACTIVADO;
    }

    /**
     * Metodo que envia email para crear nueva clave.
     * 
     * Antes de llamar a esta funcion debe llamarse a get_datos($email) 
     * para obtener los datos del usuario necesarios `codigo_acceso`, `email` y `nombre`
     * 
     * @return boolean TRUE si envia el email para cambiar clave o FALSE en caso contrario
     */
    public function recordar_clave() {
        $to = $this->email;
        $subject = 'Crear nueva clave';
        $action = 'cambiar_clave';
        $option = $this->codigo_acceso;
        $nombre = $this->nombre;
        $content = 'Para cambiar tu clave deber&aacute;s hacer clic en el siguiente link:';
        if (enviar_email($to, $subject, $action, $option, $nombre, $content)) {
            return TRUE;
        }
        return FALSE;
    }

    /**
     * Metodo que indica si el campo `cadigo_acceso` pasado por parametro, existe
     * en la tabla usuarios
     * 
     * @param string $codigo_acceso
     * @return boolean TRUE si existe FALSE en caso contrario
     */
    public function buscar_codigo_acceso($codigo_acceso) {
        $codigo_acceso = evitar_inyeccion($codigo_acceso);
        $this->query = "SELECT * FROM `$this->database`.`usuarios` "
                . "WHERE `codigo_acceso` = '$codigo_acceso'";
        $this->get_resultado_query();
        /*
         * Ya que count() 
         * devuelve 1 si $this->rows no es un array
         * Si es NULL devuelve 0
         */
        if (is_array($this->rows) && count($this->rows) === 1) {
            /*
             * Cambio estado de objeto
             */
            $this->get_datos($this->rows[0]['email']);
            return TRUE;
        }
        return FALSE;
    }

    /**
     * Metodo que cambia la clave del usuario con la introducida por parametro.
     * Ademas del campo `clave`, modificara el campo 
     * `codigo_acceso`, ya que es necesario para hacer login
     * Antes de llamar a este metodo es necesario llamar a buscar_codigo_acceso
     * que se encargara de verificar que exite tal codigo en la tabla.
     * Si existe, la propiedad rows[0] contendra los datos del usuario con ese codigo_acceso
     * 
     * @param string $clave Nueva clave del usuario
     */
    public function cambiar_clave($clave) {
        $id = $this->rows[0]['id'];
        $email = $this->rows[0]['email'];
        $clave = md5($clave);
        $codigo_acceso = $clave . md5($email);
        $clave = evitar_inyeccion($clave);
        $codigo_acceso = evitar_inyeccion($codigo_acceso);
        $this->query = "UPDATE `$this->database`.`usuarios` "
                . "SET `clave` = '$clave', `codigo_acceso` = '$codigo_acceso' "
                . "WHERE `usuarios`.`id` = $id";
        $this->ejecutar_query();
    }

    /**
     * Metodo que comprueba si el email de facebook existe en la tabla usuarios.
     * Si no existe registra un nuevo usuario con datos de $user_profile
     * 
     * @global array $user_profile Contiene los datos de usuario de facebook
     */
    public function comprobar_facebook() {
        global $user_profile;

        if (isset($user_profile['email']) && !$this->get_datos($user_profile['email'])) {
            $datos = array(
                'email' => var_facebook($user_profile['email']),
                'clave' => var_facebook($user_profile['id']),
                'nombre' => var_facebook($user_profile['first_name']),
                'apellidos' => var_facebook($user_profile['middle_name']) . ' ' . var_facebook($user_profile['last_name']),
                'foto' => 'https://graph.facebook.com/' . var_facebook($user_profile['id']) . '/picture',
                'facebook' => 1,
                'activacion' => 1
            );
            $this->insertar_datos($datos);
            return TRUE;
        }
        return FALSE;
    }

}
